package com.gogant.spider.http;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import javax.swing.text.html.HTML;
import javax.swing.text.html.HTML.Attribute;

import com.gogant.spider.http.HtmlSelector.HtmlNode;

class DefaultFieldSetter implements FieldSetter<Object> {
	private static final int GET_TEXT = 1;
	private static final int GET_TRIM_TEXT = 2;
	private static final int GET_HTML = 3;
	private Field field;
	private boolean isAll;
	private Attribute attrKey;
	private int valueMethod;
	private Method writeMethod;

	private DefaultFieldSetter(HtmlSelector selector, Field field, String value, boolean wirteByField) {
		if (value == null || value.length() == 0) {
			throw new IllegalArgumentException("value can not be null");
		}
		if (wirteByField && !Modifier.isPublic(field.getModifiers())) {
			throw new IllegalArgumentException("field must be public");
		}
		if ("text".equalsIgnoreCase(value)) {
			valueMethod = GET_TEXT;
		} else if ("trimText".equalsIgnoreCase(value)) {
			valueMethod = GET_TRIM_TEXT;
		} else if ("allText".equalsIgnoreCase(value)) {
			selector.setSelectChildText(true);
			valueMethod = GET_TRIM_TEXT;
		} else if ("html".equalsIgnoreCase(value)) {
			selector.setSelectHtml(true);
			valueMethod = GET_HTML;
		} else {
			try {
				attrKey = HTML.getAttributeKey(value);
			} catch (Exception e) {
			}
			if (attrKey == null) {
				throw new IllegalArgumentException("illegal attribute name or unsupported operation: " + value);
			}
		}
		this.field = field;
		isAll = Collection.class.isAssignableFrom(field.getType());
	}

	public DefaultFieldSetter(HtmlSelector selector, Field field, Method writeMethod, String value) {
		this(selector, field, value, false);
		if (!Modifier.isPublic(writeMethod.getModifiers())) {
			throw new IllegalArgumentException("set method must be public");
		}
		this.writeMethod = writeMethod;
	}

	public DefaultFieldSetter(HtmlSelector selector, Field field, String value) {
		this(selector, field, value, true);
	}

	private Object getTypeValue(String value, Class<?> type) throws Exception {
		if (type.equals(String.class)) {
			return value;
		} else if (type.equals(Integer.TYPE) || type.equals(Integer.class)) {
			return Integer.valueOf(value);
		} else if (type.equals(Boolean.TYPE) || type.equals(Boolean.class)) {
			return Boolean.valueOf(value);
		} else if (type.equals(Long.TYPE) || type.equals(Long.class)) {
			return Long.valueOf(value);
		} else if (type.equals(Double.TYPE) || type.equals(Double.class)) {
			return Double.valueOf(value);
		} else if (type.equals(Float.TYPE) || type.equals(Float.class)) {
			return Float.valueOf(value);
		} else if (type.equals(Short.TYPE) || type.equals(Short.class)) {
			return Short.valueOf(value);
		} else if (type.equals(Byte.TYPE) || type.equals(Byte.class)) {
			return Byte.valueOf(value);
		} else {
			throw new Exception("unsupported field type: " + type);
		}
	}

	private String getValue(HtmlNode node) {
		if (attrKey != null) {
			return node.getAttribute(attrKey);
		} else {
			if (valueMethod == GET_TRIM_TEXT) {
				return node.getTrimText();
			} else if (valueMethod == GET_TEXT) {
				return node.getText();
			} else if (valueMethod == GET_HTML) {
				return node.getHtml();
			} else {
				return null;
			}
		}
	}

	@Override
	public void set(Object obj, HtmlSelector selector) throws Exception {
		if (selector.getNodeCount() <= 0) {
			return;
		}
		Object valueObject = null;
		if (isAll) {
			Class<?> fieldType = field.getType();
			Collection<String> coll = null;
			if (fieldType == List.class || fieldType == ArrayList.class) {
				coll = new ArrayList<String>();
			} else if (fieldType == Set.class) {
				coll = new HashSet<String>();
			} else if (fieldType == LinkedList.class) {
				coll = new LinkedList<String>();
			} else if (fieldType == Vector.class) {
				coll = new Vector<String>();
			} else {
				throw new Exception("unsupported collection type or field type is not collection: " + fieldType);
			}
			for (HtmlNode node : selector.getNodeList()) {
				String value = getValue(node);
				if (value == null) {
					continue;
				}
				coll.add(value);
			}
			valueObject = coll;
		} else {
			String value = getValue(selector.getFirstNode());
			if (value == null) {
				return;
			}
			valueObject = getTypeValue(value, field.getType());
		}
		try {
			if (writeMethod != null) {
				writeMethod.invoke(obj, valueObject);
			} else {
				field.set(obj, valueObject);
			}
		} catch (Exception e) {
			throw new Exception("can't set value: " + e.getMessage());
		}
	}
}
